/*
 * 最大乘积
 *
 * 题目链接：https://programming.pku.edu.cn/probset/c4b75386cfd8411991908475c4b91bc6/ef6b04b013a041418460f2df14868288/
 * 作者：lyazj <seeson@pku.edu.cn>
 *
 * 本题所需主要知识点：
 *   - 记忆化搜索或动态规划
 *
 * 如果观察到序列的部分特征，可以实现 O(n^2) -> O(n) 的优化
 */

#include <stdio.h>
#include <string.h>

int main(void)
{
  int n;  // n >= 5
  scanf("%d", &n);

  // dp0, m0: 连续序列
  // dp1, m1: 有且仅有 1 处间隔为 2 的间断的序列
  // dp, m: 以上二者的较优情形
  double dp0[n + 1], dp1[n + 1], dp[n + 1];
  int m0[n + 1], m1[n + 1], m[n + 1];
  memset(dp0, 0, sizeof dp0);
  memset(dp1, 0, sizeof dp1);

  // n 为 1--4 时均不分解
  dp0[1] = dp[1] = m0[1] = m[1] = 1;
  dp0[2] = dp[2] = m0[2] = m[2] = 2;
  dp0[3] = dp[3] = m0[3] = m[3] = 3;
  dp0[4] = dp[4] = m0[4] = m[4] = 4;
  dp1[1] = 0, m1[1] = 0;
  dp1[2] = 0, m1[2] = 0;
  dp1[3] = 0, m1[3] = 0;
  dp1[4] = 3, m1[4] = 3;

  // n 为 5 以上时始终分解
  for(int i = 2; i <= n; ++i) {
    // 计算 dp 和 m，此处 >= 等号不可能成立
    dp0[i] >= dp1[i] ? (dp[i] = dp0[i], m[i] = m0[i]) : (dp[i] = dp1[i], m[i] = m1[i]);

    // 连续序列的状态转移
    if(i + m[i] + 1 <= n && dp0[i + m[i] + 1] < dp0[i] * (m[i] + 1)) {
      dp0[i + m[i] + 1] = dp0[i] * (m[i] + 1);
      m0[i + m[i] + 1] = m[i] + 1;
    }

    // 间断序列的状态转移
    if(i + m[i] + 1 <= n && dp1[i + m[i] + 1] < dp1[i] * (m[i] + 1)) {
      dp1[i + m[i] + 1] = dp1[i] * (m[i] + 1);
      m1[i + m[i] + 1] = m[i] + 1;
    }

    // 从连续序列到间断序列的状态转移
    if(i + m[i] + 2 <= n && dp1[i + m[i] + 2] < dp0[i] * (m[i] + 2)) {
      dp1[i + m[i] + 2] = dp0[i] * (m[i] + 2);
      m1[i + m[i] + 2] = m[i] + 2;
    }
  }

  // 计数排序输出答案
  int ans[n + 1];
  memset(ans, 0, sizeof ans);
  int i = n;
  for(;;) {
    if(m[i] == i) {
      ++ans[i];
      break;
    }
    ++ans[m[i]];
    i -= m[i];
  }
  for(i = 1; i <= n; ++i) {
    if(ans[i]) printf("%d ", i);
  }
  fseek(stdout, -1, SEEK_CUR);
  printf("\n");
  return 0;
}
